package com.nycreativity.elecreativity.controller.ijpay.alipay;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AlipayTradePagePayModel;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.ijpay.alipay.AliPayApi;
import com.ijpay.alipay.AliPayApiConfig;
import com.ijpay.alipay.AliPayApiConfigKit;
import com.nycreativity.elecreativity.auth.RequireRole;
import com.nycreativity.elecreativity.config.ijpay.alipay.AliPayBean;
import com.nycreativity.elecreativity.entity.ResultData;
import com.nycreativity.elecreativity.entity.cons.CommonURLConstants;
import com.nycreativity.elecreativity.entity.enums.OrderStatusEnum;
import com.nycreativity.elecreativity.entity.enums.PaymentMethodEnum;
import com.nycreativity.elecreativity.entity.enums.PaymentStateEnum;
import com.nycreativity.elecreativity.entity.param.order.OrderPaymentParam;
import com.nycreativity.elecreativity.entity.po.Order;
import com.nycreativity.elecreativity.entity.vo.pay.AlipayApiVO;
import com.nycreativity.elecreativity.service.OrderService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;

/**
 * @author ttxxi
 */
@RestController
@RequestMapping(CommonURLConstants.ALIPAY_PREFIX)
@Api("支付宝相关接口")
public class AliPayIJPayController extends AbstractAliPayApiController {
    private static final Logger log = LoggerFactory.getLogger(AliPayIJPayController.class);

    @Resource
    private AliPayBean aliPayBean;

    @Resource
    OrderService orderService;

    /**
     * 支付完成后支付宝会请求这个地址，由此我们可以来修改数据库中的订单状态
     */
    private final static String NOTIFY_URL = CommonURLConstants.ALIPAY_PREFIX + "/cert_notify_url";
    /**
     * 网站在支付完成后会跳转到这个地址
     */
    private final static String RETURN_URL = CommonURLConstants.ALIPAY_PREFIX + "/cert_return_url";
    /**
     * RETURN_URL的基础上重定向的地址
     */
    private final static String REDIRECT_URL = "http://123.207.28.30/NY/index.html#/receipt?";

    @Override
    public AliPayApiConfig getApiConfig() throws AlipayApiException {
        AliPayApiConfig aliPayApiConfig;
        try {
            aliPayApiConfig = AliPayApiConfigKit.getApiConfig(aliPayBean.getAppId());
        } catch (Exception e) {
            aliPayApiConfig = AliPayApiConfig.builder()
                    .setAppId(aliPayBean.getAppId())
                    .setAliPayPublicKey(aliPayBean.getPublicKey())
                    .setAppCertPath(aliPayBean.getAppCertPath())
                    .setAliPayCertPath(aliPayBean.getAliPayCertPath())
                    .setAliPayRootCertPath(aliPayBean.getAliPayRootCertPath())
                    .setCharset("UTF-8")
                    .setPrivateKey(aliPayBean.getPrivateKey())
                    .setServiceUrl(aliPayBean.getServerUrl())
                    .setSignType("RSA2")
                    // 普通公钥方式
                    //.build();
                    // 证书模式
                    .buildByCert();

        }
        return aliPayApiConfig;
    }

    /**
     * PC支付 http://localhost:8090/v1/aliPay/pcPay?orderId=1112
     */
    @GetMapping(value = "/pcPay")
    @ApiOperation("支付宝pc支付")
    @ApiImplicitParam(name = "orderId", value = "订单id", required = true)
    public void pcPay(HttpServletResponse response, @RequestParam Long orderId) {
        try {

            Order order = orderService.getById(orderId);
            if (order == null) {
                log.error("订单编号有误：" + orderId);
                return;
            }
            log.info("Ali pcPay outTradeNo>" + orderId);

            //从订单中查询总金额而不应该是外部传入
//            String totalAmount = "100";
            String totalAmount = order.getTotalAmount().toString();
            String outTradeNo = orderId.toString();

            AlipayTradePagePayModel model = new AlipayTradePagePayModel();
            //外部订单号
            model.setOutTradeNo(outTradeNo);
            //销售产品码，与支付宝签约的产品码名称。 注：目前仅支持FAST_INSTANT_TRADE_PAY
            model.setProductCode("FAST_INSTANT_TRADE_PAY");
            //交易金额
            model.setTotalAmount(totalAmount);
            //商品标题/交易标题/订单标题/订单关键字等。 注意：不可使用特殊字符，如 /，=，& 等
            model.setSubject("nycreativity.com 订单编号" + outTradeNo);
            //订单描述
            model.setBody("外部订单号：" + orderId + " 使用Alipay交易，金额为： " + totalAmount);
            //公用回传参数，如果请求时传递了该参数，则返回给商户时会回传该参数。支付宝会在异步通知时将该参数原样返回。
            //本参数必须进行UrlEncode之后才可以发送给支付宝
            model.setPassbackParams("passback_params");

            String notifyUrl = aliPayBean.getDomain() + NOTIFY_URL;
            String returnUrl = aliPayBean.getDomain() + RETURN_URL;
            AliPayApi.tradePage(response, model, notifyUrl, returnUrl);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 交易查询
     * api：https://opendocs.alipay.com/apis/api_1/alipay.trade.query#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0
     */
    @GetMapping(value = "/tradeQuery")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "orderId", value = "订单id", required = true),
            @ApiImplicitParam(name = "paymentInnerId", value = "支付内部id", required = true)
    })
    public ResultData<AlipayApiVO> tradeQuery(@RequestParam String orderId, @RequestParam String paymentInnerId) {
        try {
            //http://localhost:8090/v1/aliPay/tradeQuery?orderId=1234567890&paymentInnerId=2021041822001464020501021999
            AlipayTradeQueryModel model = new AlipayTradeQueryModel();
            model.setOutTradeNo(orderId);
            model.setTradeNo(paymentInnerId);

            JSONObject responseBody = JSONObject.parseObject(AliPayApi.tradeQueryToResponse(model).getBody());
            JSONObject alipayTradeQueryResponse = JSONObject.parseObject(responseBody.getString("alipay_trade_query_response"));
            String code = alipayTradeQueryResponse.getString("code");
            String msg = alipayTradeQueryResponse.getString("msg");
            if ("10000".equals(code)) {
                //查询成功
                String subCode = alipayTradeQueryResponse.getString("trade_status");
                //通过状态是否为TRADE_SUCCESS || TRADE_FINISHED来确认是否已支付
                boolean success = "TRADE_SUCCESS".equals(subCode) || "TRADE_FINISHED".equals(subCode);
                if (success){
                    updateOrder(orderId, paymentInnerId);
                }
                AlipayApiVO alipayApiVO = new AlipayApiVO(code, msg, subCode, null, success);
                return ResultData.SUCCESS(null, alipayApiVO);
            } else {
                //查询失败
                String subMsg = alipayTradeQueryResponse.getString("sub_msg");
                String subCode = alipayTradeQueryResponse.getString("sub_code");
                AlipayApiVO alipayApiVO = new AlipayApiVO(code, msg, subCode, subMsg, false);
                return ResultData.SUCCESS(null, alipayApiVO);
            }

        } catch (AlipayApiException e) {
            e.printStackTrace();
            return ResultData.FAILED(e.getErrMsg());
        } catch (Exception e) {
            e.printStackTrace();
            return ResultData.FAILED(e.toString());
        }
    }


    /**
     * 退款
     */
    @GetMapping(value = "/tradeRefund")
    @ApiOperation("支付宝退款")
    @RequireRole("manager")
    public ResultData tradeRefund(@RequestParam String orderId, @RequestParam String paymentInnerId) {

        try {

            Order order = orderService.getById(orderId);
            if (order == null){
                return ResultData.FAILED("订单不存在");
            }

            AlipayTradeRefundModel model = new AlipayTradeRefundModel();
            model.setOutTradeNo(orderId);
            model.setTradeNo(paymentInnerId);

            model.setRefundAmount(order.getTotalAmount().toString());
//            model.setRefundAmount("100");
            model.setRefundReason("正常退款");
            System.out.println(AliPayApi.tradeRefundToResponse(model).getBody());
            JSONObject responseBody = JSONObject.parseObject(AliPayApi.tradeRefundToResponse(model).getBody());
            JSONObject alipayTradeRefundResponse = JSONObject.parseObject(responseBody.getString("alipay_trade_refund_response"));
            String code = alipayTradeRefundResponse.getString("code");
            String msg = alipayTradeRefundResponse.getString("msg");
            if ("10000".equals(code)) {
                //查询成功
                String gmtRefundPay = alipayTradeRefundResponse.getString("gmt_refund_pay");
                //通过是否包含退款日期判断是否退款成功,原本是通过fund_change字段判断的，但是取出来的值一直有问题
                boolean success = gmtRefundPay != null;
                if (success){
                    //如果退款成功，则订单更新状态
                    OrderPaymentParam orderPaymentParam = new OrderPaymentParam(Long.parseLong(orderId), PaymentMethodEnum.ALiPay.getCode(), paymentInnerId, PaymentStateEnum.HAS_REFUNDED);
                    orderService.updateOrderPaymentInfo(orderPaymentParam);
                }

                AlipayApiVO alipayApiVO = new AlipayApiVO(code, msg, null, null,success);
                return ResultData.SUCCESS(null, alipayApiVO);
            }else {
                //查询失败
                String subMsg = alipayTradeRefundResponse.getString("sub_msg");
                String subCode = alipayTradeRefundResponse.getString("sub_code");
                AlipayApiVO alipayApiVO = new AlipayApiVO(code, msg, subCode, subMsg, false);
                return ResultData.SUCCESS(null, alipayApiVO);
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            return ResultData.FAILED(e.getErrMsg());
        } catch (Exception e) {
            e.printStackTrace();
            return ResultData.FAILED(e.toString());
        }
    }

    /**
     * api：https://opensupport.alipay.com/support/knowledge/01/201602472200
     */
    @PostMapping(value = "/cert_notify_url")
    @ApiOperation("支付宝异步回调接口（前端请忽略此接口）")
    public String certNotifyUrl(HttpServletRequest request) {
        log.error("进入cert_notify_url=======================");
        try {
            // 获取支付宝POST过来反馈信息
            Map<String, String> params = AliPayApi.toMap(request);

            for (Map.Entry<String, String> entry : params.entrySet()) {
                System.out.println(entry.getKey() + " = " + entry.getValue());
            }

            boolean verifyResult = AlipaySignature.rsaCertCheckV1(params, aliPayBean.getAliPayCertPath(), "UTF-8", "RSA2");

            if (verifyResult) {
                System.out.println("certNotifyUrl 验证成功succcess");
                // 确认支付成功，此处更新订单的支付方式、以及订单状态、内部订单号
                String outTradeNo = params.get("out_trade_no");
                String tradeNo = params.get("trade_no");

                updateOrder(outTradeNo, tradeNo);

                return "success";
            } else {
                System.out.println("certNotifyUrl 验证失败");
                return "failure";
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            return "failure";
        }
    }

    private void updateOrder(String outTradeNo, String tradeNo) {
        Order order = orderService.getById(outTradeNo);
        //如果还是待付款的状态则更新
        if (Objects.equals(order.getStatus(), OrderStatusEnum.WAITING_FOR_PAYMENT.getStatus())) {
            OrderPaymentParam orderPaymentParam = new OrderPaymentParam(Long.parseLong(outTradeNo), PaymentMethodEnum.ALiPay.getCode(), tradeNo, PaymentStateEnum.HAS_PAID);
            orderService.updateOrderPaymentInfo(orderPaymentParam);
        }
    }

    @GetMapping(value = "/cert_return_url")
    @ApiOperation("支付宝支付成功后的跳转地址（前端请忽略此接口）")
    public String certReturnUrl(HttpServletRequest request, HttpServletResponse response) {
        log.error("进入cert_return_url=======================");
        try {
            // 获取支付宝GET过来反馈信息
            Map<String, String> map = AliPayApi.toMap(request);
            for (Map.Entry<String, String> entry : map.entrySet()) {
                System.out.println(entry.getKey() + " = " + entry.getValue());
            }

            boolean verifyResult = AlipaySignature.rsaCertCheckV1(map, aliPayBean.getAliPayCertPath(), "UTF-8",
                    "RSA2");

            if (verifyResult) {
                System.out.println("certReturnUrl 验证成功");
                // 完成了支付操作（还不知道是否成功），此处更新订单的支付方式、以及内部订单号。
                String outTradeNo = map.get("out_trade_no");
                String tradeNo = map.get("trade_no");
                OrderPaymentParam orderPaymentParam = new OrderPaymentParam(Long.parseLong(outTradeNo), PaymentMethodEnum.ALiPay.getCode(), tradeNo, null);
                orderService.updateOrderPaymentInfo(orderPaymentParam);
                //重定向
                String redirectUrl = REDIRECT_URL
                        + "orderId=" + outTradeNo + "&"
                        + "paymentInnerId=" + tradeNo;
                response.sendRedirect(redirectUrl);
                return "success";
            } else {
                System.out.println("certReturnUrl 验证失败");
                return "failure";
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            return "failure";
        } catch (IOException e) {
            e.printStackTrace();
            return "redirect failed";
        }
    }

}